package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.entity.CartItem;
import com.atguigu.gmall.cart.service.CartBizService;
import com.atguigu.gmall.cart.vo.AddCartSuccessVo;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.Jsons;
import com.atguigu.gmall.common.utils.UserAuthUtils;
import com.atguigu.gmall.feign.product.SkuDetailFeignClient;
import com.atguigu.gmall.product.entity.SkuInfo;
import com.atguigu.gmall.user.vo.UserAuthInfoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author:ahang
 * @create:2022-10-20 15:21
 * @Description:
 */
@Service
public class CartBizServiceImpl implements CartBizService {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    SkuDetailFeignClient skuDetailFeignClient;

    /**
     * 添加购物车
     * @param skuId
     * @param skuNum
     * @return
     */
    @Override
    public AddCartSuccessVo addToCart(Long skuId, Integer skuNum) {
        //决定购物车需要用的key
        String cartKey = this.determinCartKey();
        //给购物车添加商品
        SkuInfo skuInfo = addCartInfo(cartKey,skuId,skuNum);


        AddCartSuccessVo successVo = new AddCartSuccessVo();
        successVo.setSkuInfo(skuInfo);
        successVo.setSkuNum(skuNum);
        return successVo;
    }

    /**
     * 给购物车添加商品
     * @param cartKey
     * @param skuId
     * @param skuNum
     * @return
     */
    private SkuInfo addCartInfo(String cartKey, Long skuId, Integer skuNum) {
        SkuInfo result = null;
        //获取购物车
        BoundHashOperations<String, String, String> cart = getCart(cartKey);
        if(cart.hasKey(skuId.toString())){
            //1.购物车中有就修改数量
            CartItem cartItem = getCartItem(cartKey,skuId);
            cartItem.setSkuNum(cartItem.getSkuNum() + skuNum);
            //判断是否超出单品的数量限制
            if(cartItem.getSkuNum() >= RedisConst.CART_ITEM_LENGTH){
                throw new GmallException(ResultCodeEnum.CART_ITEM_OVERLIMIT);
            }
            cart.put(skuId.toString(),Jsons.tostr(cartItem));
            result = convertCartItem2SkuInfo(cartItem);
        }else{
            if(cart.size() >= RedisConst.CART_SIZE){
                throw new GmallException(ResultCodeEnum.CART_SIZE_OVERLIMIT);
            }
            if(skuNum >= RedisConst.CART_ITEM_LENGTH){
                throw new GmallException(ResultCodeEnum.CART_ITEM_OVERLIMIT);
            }
            //2.没有就添加商品
            Result<SkuInfo> skuInfo = skuDetailFeignClient.getSkuInfo(skuId);
            //3.保存到购物车
            CartItem cartItem = convertSkuInfo2CartItem(skuInfo.getData());
            cartItem.setSkuNum(skuNum);
            cart.put(skuId.toString(), Jsons.tostr(cartItem)); //map中商品id为主键,商品购物车详情value
            result = skuInfo.getData();
        }

        return result;
    }

    /**
     * cartItem转换成skuinfo
     * @param cartItem
     * @return
     */
    private SkuInfo convertCartItem2SkuInfo(CartItem cartItem) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(cartItem.getId());
        skuInfo.setPrice(cartItem.getSkuPrice());
        skuInfo.setSkuName(cartItem.getSkuName());
        skuInfo.setSkuDefaultImg(cartItem.getSkuDefaultImg());
        return skuInfo;
    }

    /**
     * 从指定的购物车中获取商品
     * @param cartKey
     * @param skuId
     * @return
     */
    private CartItem getCartItem(String cartKey, Long skuId) {
        String json = redisTemplate.opsForHash().get(cartKey, skuId.toString()).toString();
        if(!StringUtils.isEmpty(json)){
            return Jsons.toObject(json,CartItem.class);
        }
        return null;
    }

    /**
     * 将商品详情转换成购物车详情
     * @param data
     * @return
     */
    private CartItem convertSkuInfo2CartItem(SkuInfo data) {
        CartItem cartItem = new CartItem();
        cartItem.setId(data.getId());
        cartItem.setSkuId(data.getId());
        cartItem.setCartPrice(data.getPrice());
        cartItem.setSkuPrice(data.getPrice());
        cartItem.setSkuDefaultImg(data.getSkuDefaultImg());
        cartItem.setSkuName(data.getSkuName());
        cartItem.setIsChecked(1); //默认状态下选中
        cartItem.setCreateTime(new Date());
        cartItem.setUpdateTime(new Date());
        return cartItem;
    }

    /**
     *绑定一个cartkey的hash操作,获取指定的购物车
     * @param cartKey
     */
    private BoundHashOperations<String, String, String> getCart(String cartKey) {
        BoundHashOperations<String, String, String> cart = redisTemplate.boundHashOps(cartKey);
        return cart;
    }

    /**
     * 判断购物车需要用的key
     * @return
     */
    @Override
    public String determinCartKey() {
        //获取用户信息
        UserAuthInfoVo info = UserAuthUtils.getUserAuthInfo();
        Long userId = info.getUserId();
        String tempId = info.getTempId();
        String key = RedisConst.CART_INFO;
        if(userId != null){
            return key+userId;
        }else{
            return key+tempId;
        }

    }

    @Autowired
    ThreadPoolExecutor poolExecutor;


    /**
     * 查询购物车列表
     * @param cartKey
     * @return
     */
    @Override
    public List<CartItem> getCartItemList(String cartKey) {
        //拿到购物车
        BoundHashOperations<String, String, String> cart = getCart(cartKey);
        List<String> values = cart.values();
        List<CartItem> cartList =
                values.stream().map(item -> Jsons.toObject(item, CartItem.class))
                        .sorted((o1,o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()))
                        .collect(Collectors.toList());

        //开启一个异步任务,去数据库查询商品
        CompletableFuture.runAsync(() -> {
            updateCartItemsPrice(cartKey,cartList);
        },poolExecutor);
        return cartList;

    }

    /**
     * 去数据库中查询商品并更新价格
     * @param cartKey
     * @param cartList
     */
    private void updateCartItemsPrice(String cartKey, List<CartItem> cartList) {
        cartList.stream().forEach(item -> {
            Result<BigDecimal> price = skuDetailFeignClient.getPrice(item.getSkuId());
            //两价格不相等修改
            if(!item.getSkuPrice().equals(price)){
                item.setSkuPrice(price.getData());
                //保存
                saveCartItem(item,cartKey);
            }
        });
    }

    /**
     * 修改购物车中的数量
     * @param skuId
     * @param num
     */
    @Override
    public void updateCartItemNum(Long skuId, Integer num) {
        //1.判断购物车的键
        String cartKey = determinCartKey();
        //2.购物车中查询指定商品
        CartItem cartItem = getCartItem(cartKey, skuId);
        cartItem.setSkuNum(cartItem.getSkuNum() + num);
        //3.保存到购物车
        saveCartItem(cartItem,cartKey);
    }

    /**
     * 保存商品到指定的购物车
     * @param cartItem
     * @param cartKey
     */
    @Override
    public void saveCartItem(CartItem cartItem, String cartKey) {
        BoundHashOperations<String, Object, Object> cart = redisTemplate.boundHashOps(cartKey);
        cart.put(cartItem.getSkuId().toString(),Jsons.tostr(cartItem));
    }

    /**
     * 修改选中状态
     * @param skuId
     * @param status
     */
    @Override
    public void updateCheckStatus(Long skuId, Integer status) {
        //判断cartkey
        String cartKey = determinCartKey();
        //找指定商品
        CartItem cartItem = getCartItem(cartKey, skuId);
        cartItem.setIsChecked(status);
        //保存
        saveCartItem(cartItem,cartKey);
    }

    /**
     * 删除购物车中的商品
     * @param skuId
     */
    @Override
    public void deleteCartItem(Long skuId) {
        String cartKey = determinCartKey();
        redisTemplate.opsForHash().delete(cartKey,skuId.toString());
    }

    /**
     * 删除选中的购物车商品
     */
    @Override
    public void deleteChecked() {
        String cartKey = determinCartKey();
        //拿到所有选中的商品
        List<CartItem> items = getCheckCartItem(cartKey);
        //判断items中是否有选中的商品
        if(items.size() > 0){
            //遍历商品,拿到所有的商品id放进一个数组
            String[] ids = new String[items.size()];
            for (int i = 0; i < items.size(); i++) {
                ids[i] = items.get(i).getSkuId().toString();
            }
            //批量删除
            BoundHashOperations<String, String, String> cart = getCart(cartKey);
            cart.delete(ids);
        }
    }

    /**
     * 获取所有选中的商品
     * @param cartKey
     * @return
     */
    @Override
    public List<CartItem> getCheckCartItem(String cartKey) {
        List<CartItem> cartItemList = getCartItemList(cartKey);

        List<CartItem> checkedList = cartItemList.stream().filter(item ->
            item.getIsChecked() == 1)
                .collect(Collectors.toList());
        return checkedList;
    }

    /**
     * 合并购物车
     * @return
     */
    @Override
    public List<CartItem> mergeCart(UserAuthInfoVo info) {
        //1.获取用户信息
        Long userId = info.getUserId();
        String tempId = info.getTempId();

        String userCartKey = RedisConst.CART_INFO + userId;
        //2.得到临时购物车中的数据,并判断是否为空
        String tempCartKey = RedisConst.CART_INFO + tempId;
        List<CartItem> tempList = getCartItemList(tempCartKey);
        if(tempList != null && tempList.size() > 0){
            //3.合并

            for (CartItem cartItem : tempList) {
                //遍历临时购物车中的商品信息,添加到指定购物车,有就添加数量
                addCartInfo(userCartKey,cartItem.getSkuId(),cartItem.getSkuNum());
            }

            //4.清空
            clearCart(tempCartKey);
        }
        //5.再次查询redis中缓存数据
        List<CartItem> itemList = getCartItemList(userCartKey);
        return itemList;
    }

    /**
     * 清空购物车
     * @param cartKey
     */
    @Override
    public void clearCart(String cartKey) {
        redisTemplate.delete(cartKey);
    }

    /**
     * 查看临时购物车时给临时购物车一个过期时间
     * @param cartKey
     */
    @Override
    public void expireTempCart(String cartKey) {
        //判断之前有过期时间没
        Long expire = redisTemplate.getExpire(cartKey);
        if(expire < 0){
            //给key设置一个过期时间
            redisTemplate.expire(cartKey,RedisConst.CART_TEMP_TTL, TimeUnit.DAYS);
        }
    }
}
